home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
PROGRAMM
/
CC_C
/
0151.ZIP
/
BUG208.ASM
< prev
next >
Wrap
Assembly Source File
|
1985-07-30
|
40KB
|
2,026 lines
title Fido's Bugger
name bugger
;
;****************************************
;* *
;* Bugger *
;* *
;* For: Fido *
;* *
;* T. Jennings 30 July 85 *
;* created 29 April 81 *
;* *
;****************************************
;
cgroup group code
dgroup group data
;
; This debugger and boot rom supports the
;following hardware:
;
;Intel SBC 86/12:
; Console on the serial port,
; 9600 baud, no parity, 8 bits
;Memory:
; 64K minimum; buggers stack and
; work space is at 0f80:0000.
; Disk boot sector loaded at 10:0
;Diskettes:
; Intel iSBC-208 at port 40h.
; 8" and 5" diskettes. AUX port
; bits controlling motor, etc.
;Hard Disk:
; A Xebec Controller on a DTC Host
; Interface, supporting a typical
; 10 meg wini, drive type = 4.
;Other: A loudspeaker tied to bit 0 of
; the 8255 Port C.
;
; Bugger inits the hardware needed to boot,
;some interrupt vectors, then autoboots the
;hard disk (A:). It makes funny noises while
;booting.
;
; If Control-C is hit, bugger jumps into
;command mode; see BUGGER.DOC for details.
;
;
;The power on jump at FFFF:0000 must be
;patched into the ROMs after burning this
;code:
;
;2716's (2K)
;
;EVEN PROM: 07f8: ea 00 fe
;ODD PROM: 07f8: 00 00
;
;2732's (4K)
;
;EVEN PROM: 0ff8: ea 00 ff
;ODD PROM: 0ff8: 00 00
;
;What is being patched in is:
;
; jmp far 0fe00:0000 ;(2716)
; jmp far 0ff00:0000 ;(2732)
;
;Baud rate for the serial port
;
baud_rate equ 9600
baud equ 768 / (baud_rate / 100)
;
;Where our data segment is:
;
dataseg equ 0f80h
page
;
;Interrupts used:
;
;
;Floppy disk read interrupt: A standard Fido
;disk BIOS table at DS:BX. Status in AL; carry
;if error. These vectors must be below 00100h,
;or 0010:0000, since thats where the boot is
;loaded, and the 1K boot sector wipes out the
;interrupt vectors.
;
FLP_INT equ 32 ;floppy access
RMS_INT equ 33 ;hard disk access
TRC_INT equ 1 ;single step
BRK_INT equ 3 ;break point
BRK_INST equ 0cch ;INT 3
CR equ 0dh ;carriage return
LF equ 0ah ;line feed
include iodef.ash
include disk.ash
include disk2.ash
page
;
;Where we load the boot sector. This is
;in the middle of the interrupt vectors,
;and it assumes the boot sector isnt big
;enough to trash our vectors.
;
bootseg segment at 10h
org 0
boot label far
bootseg ends
;
;Start of ROM code. cgroup must be
;first so the linker will put things in
;the right order.
;
code segment byte public 'code'
assume cs:cgroup
bugger: jmp start
;
;Old interface. Do not use it.
;
interface proc far
call ina
ret
call outa
ret
call foo ;deleted call,
ret ;dummy out.
call execute
ret
call dtc
ret
;
;New interface, accessed by the software
;interrupts. Call the ROM code,return,
;trashing the flags.
;
hboot: call dtc
ret 2
fboot: call execute
ret 2
interface endp
foo: ret
code ends
data segment byte public 'data'
stack dw (?)
;
;Work space for the DTC/Xebec controller
;drivers and initialization.
;
cmdtbl db 9 dup (?)
dattbl db 8 dup (?)
;
;DTC/Xebec command string.
;
xcomand db (?) ;command,
lun db (?) ;unit, top sector,
hiaddr db (?) ;high sec byte,
lowaddr db (?) ;low sec byte,
scount db (?) ;sector count,
control db (?)
db 8 dup (?)
;
;breakpoint and go register save/display area
;these register save locations must be kept in
;this order.
;
dmp_ptr label word
brkfl dw (?) ;saved flags
brkax dw (?) ;saved ax
brkbx dw (?) ;saved bx
brkcx dw (?) ;saved cx
brkdx dw (?) ;saved dx
brksi dw (?) ;saved si
brkdi dw (?) ;saved di
brksp dw (?) ;saved sp
brkip dw (?) ;saved ip
ip_base dw (?) ;reloc reg
brkcs dw (?)
brkds dw (?)
brkes dw (?)
brkss dw (?)
brkbp dw (?) ;base pointer
;
;More data area. These don't have to be in
;any specific order.
;
noreloc db (?) ;no relocation
dmpoff dw (?) ;display addr
dmpseg dw (?) ;local ES
scratch db (?) ;scratch word,
bootnum dw (?) ;default drive,
;
;Breakpoint data.
;
brkflg db (?) ;true if break
brkadr dw (?) ;brk pnt addr
brkseg dw (?) ;brk pnt seg
brkinst db (?) ;saved inst
data ends
page
code segment byte public 'code'
assume cs:cgroup,ds:dgroup
;
;Data word to set DS.
;
dataloc dw dataseg
;
;Data tables here, to avoid MASM bugs.
;
ctable db 'g'
dw offset go
db 'x'
dw offset registers;dump regs
db 'r'
dw offset registers
db 's'
dw offset memchg ;change memory
db 'e'
dw offset memchg ;ditto
db 'd'
dw offset dump ;dump memory
db 'f'
dw offset fill ;fill memory,
db 'm'
dw offset blkmov ;move memory.
db 'i'
dw offset input ;port input
db 'o'
dw offset output ;port output.
db 't'
dw offset trace ;trace command,
db 'h'
dw offset arith ;do arithmetic
db 'l'
dw offset intel ;load hex,
db 'v'
dw offset intvec ;fiddle int vec
db 'b'
dw offset dboot ;disk boot
page
eh db ' eh?',0
id1str db CR,"Fido's Bugger, 30 July 85"
id2str db CR,"Booting from drive A:, "
db " Control-C to abort: ",0
dskstr db CR,"Disk read error",0
butstr db CR,"Bad boot sector",0
prompt db CR,'Ok.',0
regstr1 db CR,'ax bx cx dx si di '
db 'sp ip + rr ',CR,0
regstr2 db CR,'cs ds es ss bp TOS '
db 'NOS ....oditsz.a.p.c',CR,0
regtbl dw 'fl','ax','bx','cx','dx','si'
dw 'di','sp','ip','rr','cs'
dw 'ds','es','ss','bp'
rtblen equ ($ - regtbl) / 2
page
;
;Disk boot read tables. These are the
;standard Fido MSDOS BIOS disk tables. ALl
;boot sectors are track 0 sector 1. (First
;sector, damn IBM)
;
;Shugart 850, drive 0, DD1024 single sided,
;double density, eight 1024 byte sectors,
;77 tracks.
;
dd1024 db fread ;read command,
db 0 ;drive 0,
dw 0 ;track 0,
dw 1 ;sector 1,
dw 1 ;1 sector,
dw -1 ;current track unknown
db 1 ;double density,
db 15 ;gap length,
db 3 ;sec size,
db 255 ;data len,
dw 1024 ;sector size,
dw 0 ;DMA offset,
dw 10h ;DMA segment,
dw 8 ;sectors/track
dw 0 ;head 0,
dblen equ $ - dd1024
;
;Shugart 850, SD128 single sided single
;density 26 128 byte sectors per track.
;
sd128 db fread ;read command,
db 0 ;drive 0,
dw 0 ;track 0,
dw 1 ;sector 1,
dw 1 ;1 sector,
dw -1 ;current track unknown
db 0 ;single density,
db 0 ;gap length,
db 0 ;sec size,
db 0 ;data len,
dw 128 ;sector size,
dw 0 ;DMA offset,
dw 10h ;DMA segment,
dw 26
dw 0
;
;Tandon DMIBM8-1 single sided double density
;eight 512 byte sectors per track, 40 tracks.
;
ibm db fread ;read command,
db 2 ;drive 2,
dw 0 ;track 0,
dw 1 ;sector 1,
dw 1 ;1 sector,
dw -1 ;current track unknown
db 3 ;double density mini
db 0 ;gap length,
db 0 ;sec size,
db 0 ;data len,
dw 512 ;sector size,
dw 0 ;DMA offset,
dw 10h ;DMA segment,
dw 8 ;spt
dw 0 ;head
page
;RMS, HD256 8 heads 32 256 byte sectors/track,
;logically arranged as 192 spt.
;
rms db fread ;read command,
db 0 ;drive 0,
dw 0 ;track 0,
dw 1 ;sector 1,
dw 1 ;1 sector,
dw -1 ;current track unknown
db 0
db 0 ;gap length,
db 0 ;sec size,
db 0 ;data len,
dw 256 ;sector size,
dw 0 ;DMA offset,
dw 10h ;DMA segment,
dw 192 ;spt
dw 0
;
;Same as above, table for interrogating
;drive ready only.
;
rmsc db fcheck ;read command,
db 0 ;drive 0,
dw 0 ;track 0,
dw 1 ;sector 1,
dw 1 ;1 sector,
dw -1 ;current track unknown
db 0
db 0 ;gap length,
db 0 ;sec size,
db 0 ;data len,
dw 256 ;sector size,
dw 0 ;DMA offset,
dw 10h ;DMA segment,
dw 192 ;spt
dw 0 ;head
page
;
;Sound tables, for a 8253 clock rate
;of 1.23 MHz.
;
OCTAVE equ 2
CNOTE equ 9609 ;C
CSNOTE equ 9044 ;C#
DNOTE equ 8541 ;D
DSNOTE equ 8092 ;D#
ENOTE equ 7687 ;E
FNOTE equ 6988 ;F
FSNOTE equ 6684 ;F#
GNOTE equ 6406 ;G
GSNOTE equ 5913 ;G#
ANOTE equ 5694 ;A
ASNOTE equ 5301 ;A#
BNOTE equ 4959 ;B
NONOTE equ 0 ;no tone
ENDNOTE equ -1 ;end of list
EIGHTH equ 20
QUARTER equ 2 * EIGHTH
HALF equ 2 * QUARTER
FULL equ 2 * HALF
;
;Something wrong
;
fault label word
dw NONOTE,QUARTER
dw DNOTE,QUARTER
dw DSNOTE,HALF
dw CNOTE,QUARTER
dw CSNOTE,HALF
dw DNOTE,QUARTER
dw DSNOTE,HALF
dw CNOTE,QUARTER
dw CSNOTE,HALF
dw ENDNOTE
;
;Power up, alive.
;
alive label word
dw CNOTE,QUARTER
dw ANOTE,QUARTER
dw GNOTE,QUARTER
dw ENOTE,QUARTER
dw NONOTE,QUARTER
dw CNOTE,QUARTER
dw ANOTE,QUARTER
dw GNOTE,QUARTER
dw ENOTE,QUARTER
dw NONOTE,QUARTER
dw ENDNOTE
;
;While trying to boot
;
bootsong label word
dw ANOTE,HALF
dw FSNOTE,HALF
dw ENOTE,HALF
dw DNOTE,HALF
dw NONOTE,HALF
dw ENDNOTE
;
;Boot loaded, running DOS
;
dostune label word
dw CNOTE,QUARTER
dw DNOTE,QUARTER
dw ENOTE,QUARTER
dw FNOTE,QUARTER
dw GNOTE,QUARTER
dw BNOTE,QUARTER
dw ENDNOTE
;
;Bugger command error
;
blatz label word
dw DNOTE,EIGHTH
dw DSNOTE,EIGHTH
dw CNOTE,EIGHTH
dw CSNOTE,EIGHTH
dw ENDNOTE
page
;
;Set things up, init the hardware, try
;to boot.
;
start: mov ss,cs:dataloc
mov sp,offset stack
mov ds,cs:dataloc
call IOINIT ;clear hardware
mov bx,offset alive
call play ;make noise
mov ax,0
mov ds,ax ;install int
mov bx,BRK_INT * 4
mov word ptr [bx],offset brkntr
mov bx,BRK_INT * 4 + 2
mov word ptr [bx],cs
mov bx,TRC_INT * 4
mov word ptr [bx],offset brkntr
mov bx,TRC_INT * 4 + 2
mov word ptr [bx],cs
mov bx,FLP_INT * 4
mov word ptr [bx],offset fboot
mov bx,FLP_INT * 4 + 2
mov word ptr [bx],cs
mov bx,RMS_INT * 4
mov word ptr [bx],offset hboot
mov bx,RMS_INT * 4 + 2
mov word ptr [bx],cs
mov ds,cs:dataloc
mov ip_base,0
mov brkflg,0
mov brkds,ds
mov brkss,ss
mov brksp,sp
mov bx,offset id1str
call pstr
;
;Wait until the disk is ready, then boot it.
;If Control-C is typed, jump into bugger.
;
mov ax,cs ;disk tables in
mov ds,ax ;code seg
b11: mov bx,offset bootsong
call play ;chance to hit
call inastat ;Control-C
jz b13
call ina
cmp al,3
jne b13
mov bx,offset fault
call play ;make noise
jmp warm ;exit if ^C
b13: mov bx,offset rmsc ;check for
int RMS_INT ;drive ready
jc b11
mov bx,offset rms ;attempt to
int RMS_INT ;read the boot
jnc b20
mov bx,offset dskstr;if error,
call pstr ;nasty message
mov bx,offset fault
call play
jmp b11
b20: mov ax,bootseg ;read OK,
mov ds,ax ;make sure it's
mov bx,0 ;really a boot
mov al,[bx] ;sector,
cmp al,0fah ;(CLI)
je b21 ;if not,
mov bx,offset butstr
call pstr
mov bx,offset fault
call play
jmp b11
b21: mov bx,offset dostune
call play
mov cx,0 ;drive A:,
jmp boot ;boot system.
page
;
;This is the main command loop, where commands
;are input, etc. Any user type errors get here
;also. Some commands that cannot maintain stack
;discipline, such as return from break point,
;jump here. Look up single letter commands
;input from the console, in the command table.
;If we find one, go execute it, else barf.
;
warm: mov ss,cs:dataloc
mov sp,offset stack
mov ds,cs:dataloc ;set our DS,
mov es,dmpseg ;and ES,
wig: mov bx,offset prompt ;type prompt,
call pstr ;string in CS:
call charin ;get command
jz wig ;ignore delims
mov bx,offset ctable;table search,
warma: mov ah,cs:[bx] ;table entry
mov cx,cs:[bx + 1] ;call addr
add bx,3 ;next ...
cmp ah,0 ;end of table?
je error
cmp ah,al ;command key?
jne warma
call cx ;call it,
jmp warm ;repeat.
;
;General purpose error handler. not real
;graceful.
;
error: mov ss,cs:dataloc
mov sp,offset stack
mov bx,offset eh ;nasty msg,
call pstr
mov bx,offset blatz
call play
jmp warm
page
;
;Trace command. Set the Trace flag in the flag
;register, then execute like a 'go'.
;
trace: or brkfl,0000000100000000b
jmp gonow ;go execute,
;
;Go with or without breakpoint. If a comma is
;entered, get a breakpoint address, else just
;execute.
;
go: and brkfl,1111111011111111b
mov es,brkcs ;set 'go' seg
mov dmpseg,es ;and in ES,
mov bx,brkip ;and 'go' off
mov dmpoff,bx
call getadr ;for 'getadr'
mov brkip,bx ;set brk IP,
mov brkcs,es ;and seg,
cmp al,',' ;if a comma,
jne gonow ;breakpoint,
;
;The start addr is set, and a comma was typed.
;Get the breakpoint addr, put an INT 3 there
;and save the instruction.
;
gobrk: call getadr ;get brk addr
mov brkadr,bx ;save brk adr
mov brkseg,es
mov al,BRK_INST
xchg al,es:[bx] ;set brk inst,
mov brkinst,al ;save old inst
mov brkflg,1 ;set flag,
;
;Restore regs and return.
;
gonow: cli ;no ints,
mov ax,brkax
mov bx,brkbx ;restore a
mov cx,brkcx ;bunch of regs
mov dx,brkdx
mov di,brkdi
mov si,brksi
mov bp,brkbp
mov ss,brkss ;set SS and
mov sp,brksp ;stack pointer,
push brkfl ;push flags,
push brkcs ;far address,
push brkip
mov es,brkes ;restore ES,
mov ds,brkds ;and DS,
iret ;call address
page
;
;Breakpoint entry. Executed only via a jump
;from the restart location. Breakpoints
;require lots of the users stack. All registers
;are saved and displayed.
;
;Decrement the go counter; if not zero yet,
;display the regs and go reexecute from there.
;
brkntr: push ds
mov ds,cs:dataloc ;set DS,
pop brkds ;save DS,
pop brkip ;IP,
pop brkcs ;CS,
pop brkfl ;flags,
mov brkax,ax ;save regs,
mov brkbx,bx
mov brkcx,cx
mov brkdx,dx
mov brksi,si
mov brkdi,di
mov brkbp,bp
mov brkes,es
mov brkss,ss
mov brksp,sp
mov ss,cs:dataloc ;set stack,
mov sp,offset stack
sti ;enable ints,
mov ax,brkip ;set variables
mov dmpoff,ax ;for debug,
mov ax,brkcs
mov dmpseg,ax
test brkflg,1 ;breakpoint?
jz bknbh ;return if not.
dec brkip ;put pc back
mov bx,brkadr ;restore the
mov es,brkcs ;instruction
mov al,brkinst
mov es:[bx],al
bknbh: mov brkflg,0 ;no more brkpt
call regdmp ;dump the regs
jmp warm ;and stop.
page
;
;Register control command. X<CR,comma,space>
;displays all registers. X<reg name> displays
;the current register value, and allows
;entering al new value. The legal register
;names are in al table in the byte section.
;
registers:
call charin
je regdmp ;Z set if delim
jmp regchg ;if not a delim
regdmp: mov bx,offset regstr1 ;type top lin
call pstr ;of the display
mov bx,brkax
call outhex ;type ax,
mov bx,brkbx
call outhex ;type bx,space
mov bx,brkcx
call outhex ;type cx,space
mov bx,brkdx
call outhex ;type bx,spaces
mov bx,brksi
call outhex ;si, spaces,
mov bx,brkdi
call outhex ;di, spaces,
mov bx,brksp
call outhex ;type sp
mov bx,brkip ;type IP,
sub bx,ip_base ;minus offset,
call outhex
mov bx,ip_base
call outhex ;reloc reg,
mov bx,offset regstr2 ;next line,
call pstr
mov bx,brkcs ;type seg regs
call outhex
mov bx,brkds
call outhex
mov bx,brkes
call outhex
mov bx,brkss
call outhex
mov bx,brkbp ;and the BP reg
call outhex
mov es,brkss ;get two top
mov si,brksp ;stack values,
cld
mov bx,es:[si]
call outhex ;type TOS,
mov bx,es:[si+2]
call outhex ;then NOS,
mov ax,brkfl
call axtobin ;type flags,
ret
page
;
;Modify pseudo registers. The registers are
;hardcoded in the same order as they appear in
;RAM. AL contains the first letter of the
;register, get the second letter in AH, then
;look it up.
;
regchg: mov ah,al
call charin
cmp ax,'ip' ;special case
je regip ;IP,
mov si,offset regtbl ;reg names,
mov bx,offset dmp_ptr; reg values,
mov cx,rtblen ;table length,
rchal: cmp ax,cs:[si] ;match?
je pair ;go change.
add si,2 ;next...
add bx,2
loop rchal
jmp ERROR ;none!
;
;Change a word in memory.
;
pair: mov cx,[bx] ;get value,
xchg bx,cx ;to BX,
call xreg ;change it,
xchg cx,bx ;to CX,
mov [bx],cx ;update.
ret
;
;Display a value, and get a new one.
;
xreg: call outhex ;display it,
mov al,'='
call charout
call inhex ;get a new one.
ret
;
;Register IP. Display current IP minus
;'ip_base', add it in when storing it.
;
regip: mov bx,brkip
sub bx,ip_base ;adjust,
call xreg ;change,
add bx,ip_base ;adjust again,
mov brkip,bx ;store.
ret
page
;
;display memory as 8 lines of 16 bytes,
;followed by 16 of ASCII. es: is our data
;segment pointer.
;
dump: call getadr ;get address,
mov cx,8 ;do i= 1,8
dmpal: call crlf ;newline,
call outadr ;type addr,
push cx ;save count,
mov cx,16 ;do j=1,16
push bx ;save for ASCII
dmpbh: mov al,es:[bx]
call out2h
inc bx
mov dmpoff,bx
loop dmpbh ;do 16 bytes
pop bx ;now dump ASCII
mov cx,16
dmpbl: mov al,es:[bx]
inc bx ;same bytes,
and al,7fh
cmp al,' '
jae dmpd
mov al,'_'
dmpd: call charout
loop dmpbl
pop cx ;get line count
loop dmpal ;next line,
ret
page
;
;Examine/change memory. An address is entered,
;and bytes are displayed after the address.
;New values may be entered, or the left
;unchanged by typing only al delimiter. Comma
;and space close the current location, and open
;the next location. A <CR> closes the current
;location and quits. Typing bad hex aborts the
;current location.
;
memchg:
call getadr ;get start addr
memal: call crlf
call outadr ;type address,
mov dmpoff,bx ;save addr,
mov al,es:[bx] ;get old,
call out2h ;type old,
mov al,es:[bx]
mov bl,al ;put old in bl,
call inhex ;maybe get new,
mov dh,bl ;put new in dh,
mov bx,dmpoff ;get addr again
mov es:[bx],dh ;write to mem
cmp al,CR ;was char a CR?
jz sret ;exit if so.
inc bx ;next location.
cmp al,LF ;if it was LF,
jnz memal ;dec instead of
dec bx ;incrementing.
dec bx
jmp memal
sret: ret
page
;
;fill memory with al number. get two addresses,
;then the filler. Cannot fill across segment
;boundaries.
;
fill: cld
call getadr
call inhex ;get 2nd addr
cmp al,':' ;dont allow 2nd
jne fllok ;segment,
jmp error ;count 64k max.
fllok: mov cx,dmpoff ;cx is lo addr,
sub bx,cx ;bx is hi addr,
xchg bx,cx ;cx is count,
push bx ;bx is lo addr
call ghex0 ;get fill char,
mov al,bl
pop di ;get addr,
jcxz sret ;dont do none,
rep stosb ;do it.
ret
;
;Move a block of memory. Get the <from>, <end>
;and <to> addresses, calculate the block length
;then move it. Moves correctly across segments.
;
blkmov: cld
call getadr ;get from,
mov dx,ES ;save seg,
mov si,bx
call inhex ;get end,
cmp al,':' ;dont allow a
jne blk1 ;segment here,
jmp error ;error!
blk1: mov cx,si
sub bx,cx ;cnt=end-from
mov cx,bx
call getadr ;get to,
mov di,bx ;ES is dest seg
push ds ;save our DS
mov ds,dx ;set our temp
rep movsb ;source segment
pop ds ;restore DS
ret
page
;
;Port I/O commands. These do 16 bit I/O ports,
;but only 8 bit data.
;
; Input data from a port.
;
input:
call ghex0 ;get port num
mov dx,bx
in al,dx ;read the port,
push ax
call crlf
pop ax
push ax
call out2h ;display in hex
pop ax
call altobin ;then in binary
ret
;
; Output data to a port.
;
output:
call ghex0 ;get port num
mov dx,bx
call ghex0 ;get the data,
mov ax,bx
out dx,al ;send it.
ret
;
;Do some simple hex arithmetic. Input an
;expression, display the result.
;
arith: call inhex ;get the number
call crlf ;newline,
call outhex ;display result
ret
page
;
;Load an intel hex file from the console. This
;does not support any extended hex format; just
;the old fashioned 8 bit hex. The segment is
;input from the console.
;
intel: mov dmpoff,0 ;set defaults,
mov dmpseg,40h
call getadr ;get load seg,
mov es,dmpseg
;
;Load each hex record to segment ES, with an
;offset of (dmpoff)( i.e. added to the specifie
;load address every time)
;
getrcd: call charin ;wait for colon
cmp al,':'
je iproc
cmp al,LF ;if LF, echo it
jne getrcd
call charout
jmp getrcd
iproc: mov dx,0 ;clear checksum
call in2 ;get the count,
mov cl,bl ;put in CX,
mov ch,0
call in2 ;get load addr
mov al,bl ;save hi addr,
call in2
mov bh,al ;assemble word,
mov di,bx ;put in DI,
call in2 ;record type,
cmp bl,1 ;EOF?
jz itret ;return if so,
jcxz itret ;or if no data,
;
;Read <CX> bytes to memory.
;
add di,dmpoff ;add in offset,
getb: call in2 ;get a byte,
mov es:[di],bl ;store it,
inc di ;next address,
loop getb
call in2 ;get check sum,
;
;checksum broken. Too lazy to fix it.
;
; add dl,bl ;if OK,
; jz getrcd ;repeat,
jmp getrcd ;patch fix.
iterr: jmp error ;else error.
itret: ret
page
;
;INTEL, continued.
;
;
;Input the next two digits as hex, return in
;BL. Accumulate a checksum in DX, preserve
;AX, CX.
;
in2: push ax
push cx
mov bx,0
call charin ;get a char,
call hexin ;install char,
jb iterr
call charin
call hexin
jb iterr
pop cx
pop ax
add dl,bl ;checksum
ret
page
;
;Examine/change an interrupt vector. The
;input number is checked for 0-ff, the
;address of it made, the contents displayed.
;The contents can be changed by typing in
;the new seg:off, or CR to leave unchanged.
;
intvec: call ghex0 ;get int num,
call crlf ;newline,
and bx,255 ;0-255,
shl bx,1 ;times 2,
shl bx,1 ;times 4,
mov ax,0
mov es,ax ;seg 0,
push bx ;save vec addr
push es ;in es:BX,
push ip_base ;relocation
mov ip_base,0 ;off,
call outadr ;display addr,
mov ax,es:[bx+2] ;get int seg,
mov bx,es:[bx] ;get int off,
mov es,ax ;to es:BX
mov dmpseg,es ;Set default
mov dmpoff,bx ;for 'getadr',
call outadr ;display it,
call getadr ;get new,
mov cx,es ;copy es:BX to
mov dx,bx ;CX:DX,
pop ip_base ;restore reloc,
pop es ;get back int
pop bx ;vector addr,
mov es:[bx+2],cx ;store new
mov es:[bx],dx ;vector.
ret
page
;
;Boot either a hard disk or floppy. Get either
;'f', 'h' or 'm', then a number. Attempt to
;read the boot sector to memory, then execute
;it, unless the number has bit 15 set.
;
dboot: call charin ;get disk,
mov si,offset btbl ;search table
db1: cmp al,cs:[si] ;find it?
je db2
add si,blen ;no, next..
cmp byte ptr cs:[si],0
jne db1 ;no more?
jmp error
db2: call ghex0 ;get number,
mov ax,cs ;disk tables
mov ds,ax ;in code seg
push bx ;save it,
mov bx,cs:[si + 1] ;disk table
call word ptr cs:[si + 3] ;read disk
pop cx
mov ds,cs:dataloc ;set DS
mov bx,offset dskstr
jc db4 ;if OK,
test cx,8000h ;and MSB set,
jz db3
jmp warm ;dont boot
db3: mov bx,bootseg ;look at
mov es,bx ;1st instruct,
mov bx,0 ;must be CLI,
cmp byte ptr es:[bx],0fah
mov bx,offset butstr
jne db4
call boot ;attempt it,
mov bx,offset dskstr
db4: call pstr
jmp warm
btbl db 'f'
dw dd1024
dw offset cgroup:execute
blen = $ - btbl
db 'd'
dw dd1024
dw offset cgroup:execute
db 's'
dw sd128
dw offset cgroup:execute
db 'h'
dw rms
dw offset cgroup:dtc
db 'm'
dw ibm
dw offset cgroup:execute
db 0
page
;
;get an address, or use dump address. If a
;segment is specified, set ES. Return AL =
;delimeter character.
;
;This adds ip_base to BX before return. RR
;defaults to 0000 at power up. Does not affect
;dmpoff so that setting RR to 0000 again
;correctly displays the right value.
;
getadr: mov ah,1 ;set flag,
mov bx,dmpseg ;default seg
call inhex ;if we set seg
cmp al,':'
jne setadr
mov dmpseg,bx ;update ES,
mov ES,bx
mov bx,dmpoff
call inhex ;then set the
jmp gtaret ;address,
setadr: dec ah ;not seg, if BX
jnz gtaret ;changed return
mov bx,dmpoff ;new else get
gtaret: mov dmpoff,bx ;old number,
test noreloc,1 ;if flag set,
jnz gtret ;dont offset.
add bx,ip_base ;add in offset,
gtret: ret
;
; type bx as hex, followed by a space.
;
outhex: call typehex
jmp o2spc
typehex:mov al,bh ; most first
call out2
mov al,bl ; do least
call out2
ret
;
;display an address; SSSS:AAAA
;
outadr: push bx
mov bx,es ;print
call typehex ;segment:offset
mov al,':'
call charout
pop bx ;save BX,
push bx
sub bx,ip_base ;adjust for
call outhex ;display,
pop bx ;restore,
ret
page
;
;type al as hex.
;
out2: push ax
push cx
mov cl,4
shr al,cl
pop cx
call out1h
pop ax
;
;type lsnybble of al.
;
out1h: and al,0fh ;only ls 4 bits
or al,'0' ; make ascii
cmp al,'9'+1
jb out1
add al,'a'-'9'-1 ; 0-9,al-f
out1: jmp charout
;
; output al as hex followed by a space.
;
out2h: call out2 ;type al in hex
;
;type a space
;
o2spc: mov al,' '
jmp out1
page
;
;Input a hex into BX, default the number to
;zero.
;
ghex0:
mov bx,0 ;fall through
;
;Input a hex number to BX. If only a delimiter
;is typed, return with no change in BX or AH.
;If any number is input, AH is cleared.
;Supports the following special characters:
;
; .<reg> value of register <reg>
; +<xxxx> sum of current value and
; additional value <xxxx> + may
; be entered after any number of
; digits are typed, including dot
; - same as above, except
; difference of 1st and 2nd value
; (first-second)
; * Return theproduct of the current
; number and the next entered
; number.
; ! Suppresses relocation of offset
; by GETADR. Cleared on each call.
;
;Be careful when changing, as it uses
;recursion. Note that 'scratch' stores the LAST
;delimiter and is not saved each iteration.
;Jumps to error if anything goes wrong.
;
inhex: mov noreloc,0 ;clear flag,
call charin ;get char
jne inha ;exit on CR,
jmp inhret
inha: mov bx,0 ;else seed it
inhb: mov ah,0 ;mark changed.
cmp al,'+' ;check special
jne inm ;chars, if plus
push bx ;save current,
call inhex ;get another,
mov scratch,al ;save delim,
pop ax
add bx,ax ;add in new one
;splice after "ah,0 "
mov ah,0 ;change flag,
mov al,scratch ;get delim
ret ;return now.
inm: cmp al,'-' ;if minus,
jne instar
push bx ;save first val
call inhex ;get second,
mov scratch,al ;save delim
mov ax,bx
pop bx
sub bx,ax ;first-second,
mov ah,0
mov al,scratch
ret
instar: cmp al,'*' ;if star,
jne indot
push cx ;save regs,
push dx
push bx ;save current,
call inhex ;get next,
mov scratch,al ;save delim,
pop ax ;get last,
mov cx,bx ;mult to CX,
mul cx ;product,
mov bx,ax ;to BX,
pop dx
pop cx ;restore,
mov al,scratch
mov ah,0 ;BX changed.
ret
indot: cmp al,'.' ;if dot, use
jne insup ;a register,
call regval ;get reg value,
jnc indot1 ;if not found,
jmp error
indot1: mov ah,0
jmp nxtchr
insup: cmp al,'!' ;if suprise,
jne notspec
mov noreloc,1 ;suppress off
jmp nxtchr
notspec:call hexin ;digit to hl,
jae nxtchr ;hex error?
jmp error ;garbage.
nxtchr: call charin ;another char
jnz inhb ;continue,
ret ;until a delim.
page
;
;check the char in (al) for hex, return carry
;if not. install digit into bx.
;
hexin: sub al,'0' ;make hex,
jz hin ;ok if 0,
jb inhret ;if .lt. 0,
hxc: cmp al,10 ;do if .le. 9,
jb hin
sub al,27h ;if a-f, hex.
cmp al,16 ;carry if below
cmc ;now carry if
jb inhret ;above,
hin:
add bx,bx ;times 16,
add bx,bx
add bx,bx
add bx,bx
or bl,al ;add in digit,
clc ;no error.
inhret: ret
;
;Get the ASCII name of a register, and return
;it's current value in BX. Return carry set if
;not a legal register.
;
regval: push si
push cx
call charin ;get 1st char,
mov ah,al ;MS byte,
call charin ;AX is name,
cmp ax,'ip' ;special case
je retip ;IP,
mov si,offset regtbl;reg names,
mov bx,offset dmp_ptr; reg values,
mov cx,rtblen
rv1: cmp ax,cs:[si] ;match?
je rv2 ;go change.
add si,2 ;next...
add bx,2
loop rv1
stc ;not found,
jmp rv3 ;return error.
rv2: mov bx,[bx] ;get value,
clc ;return good,
rv3: pop cx ;restore,
pop si
ret
;
;Return the value of IP adjusted by 'ip_base'.
;
retip: mov bx,brkip
sub bx,ip_base
clc
jmp rv3
page
;
;Type ax in binary.
;
axtobin:mov cx,16
jmp nnbit
;
;Type al in binary. Type 1's as 1, 0's as 0.
;
altobin:mov cx,8
mov ah,al
nnbit: mov dl,'0'
shl ax,1
adc dl,0
push ax
mov al,dl
call charout
pop ax
loop nnbit
jmp o2spc
;
; print a string.
;
pstr: mov al,cs:[bx]
test al,al
je psret
call charout ;type until a 0
inc bx
jmp pstr
;
;do a crlf
;
crlf: mov al,cr
call charout
psret: ret
page
;
;Wait for a character, and return in AL. Leave
;the Z bit set if the char is one of our
;delimiters. Convert to lower case.
;
charin:
call INA ;get one char
and al,7Fh
cmp al,'9'+1 ;to lower
jb chial ;case,
or al,20h ;set shift bit,
chial: cmp al,LF ;dont echo LF's
jz inaret ;return Z set,
push ax ;indicate delim
call OUTA ;echo it,
pop ax
;
;Here we check for the legal hexadecimal number
;delimiters.
;
cmp al,CR ;check delim,
jz inaret
cmp al,','
jz inaret
cmp al,' '
jz inaret
cmp al,LF
jz inaret
cmp al,':'
inaret: ret
;
;Output a character to the console.
;
charout:
cmp al,CR ;if CR, do a LF
jnz out
call out
mov al,LF
out: call OUTA
ret
page
;
;init each device to the minimum necessary
;to run the debugger. The BIOS will fully
;init everything once booted.
;
ioinit: mov al,8ah ;8255 0, A out
out ppictl,al ;B in C0-3 out
mov al,0 ;C4-7 in
out ppia,al ;outputs off
mov al,0Eh
call set8251 ;dummy mode
mov al,40h
call set8251 ;reset 8251
mov al,4Eh
call set8251 ;8 bt async *16
mov al,37h
call set8251 ;enable Tx & Rx
mov al,0B6h
out pitmode,al ;ch.2 sq wave
mov ax,baud
out pittmr2,al ;low baud rate
mov al,ah
out pittmr2,al ;high baud rate
;
;Setup 8259 Programmable Interrupt Controller
;
mov al,13h
out pici1,al ;8086 mode
mov al,00h
out pici2,al ;vector
mov al,1Fh
out pici2,al ;EOI master
mov al,0FFh
out pici2,al ;all levels off
page
;
;Initialize the RMS and DTC86. Do a
;considerable delay here, since I think theres
;a problem with the Xebex powering up slowly.
;
mov cx,-1
xdel: jmp $ + 2
loop xdel
call dskinit ;init RMS disk,
;
;Initialize the floppy controller.
;
call flpinit
ret
;
;Set a control byte to the 8251, delaying
;after each one.
;
set8251:
out pcimode,al
mov cx,100
s8: jmp $ + 2
loop s8
ret
page
;
;Character drivers.
;
;
outa: push ax
opoll: in al,pcimode
and al,1
jz opoll
pop ax
out pcidata,al
ret
;
;Input a character from the console. Turn
;interrupts off, so we can get chars while
;MSDOS is running.
;
ina: pushf
cli
inw: call inastat
jz inw
in al,pcidata
popf
inret: ret
inastat:
in al,pcimode
and al,2
ret
page
;
;Play a tune; BX points to a sound table.
;
play: mov dx,cs:[bx] ;get a note,
mov cx,cs:[bx + 2] ;get duration
add bx,4
cmp dx,ENDNOTE ;quit if end
je plz
call note
jmp play
plz: ret
;
;Play a note; BX is the divisor, CX is the
;duration. If the note is 0, pause.
;
note: mov al,0
out ppic,al ;sound off,
cmp dx,NONOTE ;if a sound,
je sd1
mov al,36h ;timer 0 mode 3
out pitmode,al
mov al,dl ;select sound
out pittmr0,al
mov al,dh
out pittmr0,al
mov al,1
out ppic,al ;enable sound,
sd1: mov ax,1000
sd2: dec ax ;delay
jnz sd2
loop sd1
mov al,0 ;disable sound
out ppic,al
ret
page
;
;Initialize the RMS disk and Xebec controller.
;Assumes an RMS 512; this is adequate for
;booting the system. The BIOS will init it
;for the correct number of tracks, etc.
;
cyls equ 153 ;cylinders
heads equ 8 ;heads,
comptrk equ 77 ;precomp trk,
ecclen equ 11 ;ECC length,
;
;initialization tables. These get copied to
;RAM, so it can be DMA'd to the DTC.
;
romtbl label byte
db 12,0,0,0 ;init drv
db 0,0,0,0,0 ;characteristic
;
;Initialization data.
;
db high cyls
db low cyls
db heads
db high comptrk
db low comptrk
db high comptrk
db low comptrk
db ecclen
tblen equ $ - romtbl
dskinit:
mov bx,offset cmdtbl ;copy the
mov si,offset romtbl ;table to RAM
mov cx,tblen ;so we can DMA
dn1: mov al,cs:[si]
mov [bx],al
inc si
inc bx
loop dn1
in al,ccr ;clr old DONE,
mov ax,ds
mov cx,16
mul cx
add ax,offset cmdtbl
adc dx,0
out cal,al
mov al,ah
out cah,al
mov al,dl
out cat,al
mov ax,ds
mul cx
add ax,offset dattbl
adc dx,0
out dal,al
mov al,ah
out dah,al
mov al,dl
out dat,al
mov al,0
out csr,al ;start it,
ret
page
;
;Intel iSBC 208 Diskette Controller.
;
;Floppy disk initialization. Set both drives
;as in service, and set the step and settle
;times for the NEC chip.
;
flpinit:
mov dx,resp ;reset port
out dx,al ;hit it
mov cx,5000
di1: jmp $ + 2
loop di1 ;short delay
mov al,0
mov dx,dcmdp
out dx,al ;DMA command
mov al,specc ;specify,
call outcmd
mov al,srthut
call outcmd
mov al,hltnd
call outcmd
ret
page
;
;Returns Z set if the drive was ready, with the
;floppy status byte in DL. The caller must
;perform additional error checking.
;
execute:
out dflp,al ;first/last
call seldrv ;select it,
jc rwz
call seek ;seek track,
jc rwz ;ret if error,
call diskio ;try it,
rwz: ret
;
;Put the count to the 8257, adding in the right
;DMA direction bit, depending on the floppy
;command.
;
diskio: mov dx,0
mov ax,secsiz[bx] ;make a DMA
mul word ptr count[bx] ;count,
dec ax ;-1 for DMA,
mov dx,ch0cp
out dx,al ;out LS byte,
mov al,ah
out dx,al ;out MS byte
mov al,dmarb ;DMA read
cmp byte ptr command[bx],fwrite
je setdm ;if disk write,
mov al,dmawb ;else disk read
setdm: mov dx,modp ;Mode Port,
out dx,al ;output it,
;
;The 208 has a nice segment register. Just
;output it.
;
mov ax,dmaoff[bx]
mov dx,ch0ap
out dx,al
mov al,ah
out dx,al
mov ax,dmaseg[bx]
mov dx,seglp
out dx,al
mov al,ah
mov dx,seghp
out dx,al
page
;
;Issue the floppy command, and wait for
;completion. Return status in DL.
;
adrok: call sense_int ;flush FDC,
mov al,dmacb
mov dx,maskp ;start DMA,
out dx,al
ak1: mov al,readc ;assume read,
mov ah,0 ;single density
cmp byte ptr command[bx],fwrite
jne ak2
mov al,writec
ak2: test byte ptr density[bx],1
jz ak3
mov ah,40h
ak3: or al,ah
call drvcmd ;cmd + drive,
mov al,track[bx]
call outcmd ;track,
mov al,head[bx]
call outcmd ;head,
mov al,sector[bx]
call outcmd ;sector,
mov al,enn[bx]
call outcmd ;recd size,
mov al,sector[bx]
add al,count[bx] ;next recd,
dec al ;last recd,
call outcmd ;last sec,
mov al,gaplen[bx]
call outcmd ;gap length,
mov al,dtl[bx]
call outcmd ;data length,
call getresult ;check results
mov al,4 ;"read error"
jc akz ;nope, check
and ah,7fh ;ST1,
stc
jnz akz
xor al,al ;no error
akz: ret
page
;
;Select the drive, return carry set if not
;ready. All this really does is select the
;drive size, and force READY true if mini.
;If the motor is off and this is a mini
;floppy, turn it on and wait.
;
seldrv: mov dx,drcp ;control port
mov al,0 ;assume 8"
test byte ptr density[bx],2
jz sv1 ;else 5 1/4"
mov al,MTR + RDY + MINI
sv1: out dx,al ;write it
mov cx,65535
sv2: jmp $ + 2
loop sv2
ret
;
;Seek a track. If current track is unknown,
;recal the drive first. Return carry set if
;error, with AL =6. (MSDOS seek error)
;
seek: mov al,curtrk[bx] ;if current
cmp al,255 ;unknown,
jne sk1 ;recal first,
call recal
jc sk2 ;quit if error,
mov al,0 ;at trk 0
sk1: cmp track[bx],al ;if at track,
je skdn ;dont seek
mov al,seekc ;seek desired
call drvcmd ;track,
mov al,track[bx]
call outcmd
mov cl,track[bx] ;for compare,
mov ch,0f8h ;status bits,
call complete
skdn: ret
;
;Recal the disk.
;
recal: mov al,recalc
call outcmd
mov al,drive[bx]
call outcmd ;recal,
mov cl,0 ;new trk =0,
mov ch,20h ;chk only seek,
call complete ;check,
xor al,al ;no errors!
ret
page
;
;Issue command in AL, followed by the
;drive number.
;
drvcmd: call outcmd ;send cmd byte,
mov al,head[bx] ;make head bit,
shl al,1
shl al,1
and al,4 ;make it legal,
or al,drive[bx] ;then add drive
call outcmd
ret
;
;Wait for seek done, then check status.
;
complete:
call sense_int ;wait for done,
cmp ah,80h ;wait for seek,
je complete
and ah,ch ;chk completion
cmp ah,20h
jne sk2 ;return if bad
cmp al,cl ;correct track?
jnz sk2 ;error if not,
ret
sk2: stc
mov al,6 ;"seek error"
ret
;
;Sense interrupt status. Return ST0 in
;AH, PCN in AL. (If no seek done, AH= 80h,
;AL= 80h.) If we find a ready line change,
;loop to clear it.
;
sense_int:
mov al,skstc ;get int status
call outcmd
call instat ;get ST0,
mov ah,al ;save it,
cmp ah,80h ;if not pending
je siz ;exit,
call instat ;get PCN,
push ax
and ah,0c0h ;if ready line,
cmp ah,0c0h ;repeat,
pop ax
je sense_int
siz: ret
page
;
;Output a byte to the FDC; delay, then wait
;for RQM and DIO == 0. IF we find DIO true, ie.
;bytes to read, read them just so we wont hang
;forever.
;
outcmd: push ax ;save the byte
oc0: call delay
mov dx,fdcstat
oc1: in al,dx
test al,80h ;wait for RQM
jz oc1
mov dx,fdcdata ;DX = data port
test al,40h ;if DIO = in,
jz oc2
in al,dx ;flush input
jmp oc0 ;bytes (?!)
oc2: pop ax
out dx,al ;output command
ret
;
;Read an input byte from the 765. If it wants
;an output byte, well, feed it something, its
;better than hanging forever.
;
instat: call delay ;slow ...
ic0: mov dx,fdcstat
ic1: in al,dx
test al,80h ;RQM
jz ic1 ;wait for it
mov dx,fdcdata
test al,40h ;DIO
jnz ic2 ;if DIO == 0
out dx,al ;satisfy it
jmp ic0
ic2: in al,dx ;read a byte
ret
;
;Get result phase. Just get bytes until there
;are no more. Return carry set if we dont get
;exactly seven input bytes. We return ST1 in AH
;and throw away the rest.
;
getresult:
mov cx,0 ;bytes read
mov ah,0ffh ;ST1
call delay
gr1: mov dx,fdcstat ;wait for RQM
in al,dx
test al,80h
jz gr1
test al,40h ;DIO set?
jz gr2
mov dx,fdcdata ;read a byte
in al,dx
cmp cx,1 ;2nd byte?
jne gr1a
mov ah,al ;save ST1
gr1a: inc cx ;count it,
jmp gr1
gr2: sub cx,7 ;check for < 7,
ret
;
;Software delay for use with fast 8086's.
;
delay: push cx ;11
mov cx,40 ;(4 to 8) * 40,
dly: jmp $ + 2 ;clear the queue
loop dly ;17/5
pop cx ;8
ret ;8
page
;
;DTC-86 / Xebec Controller driver. This assumes
;an RMS drive attached as drive 0.
;Accepts a standard Fido disk table at
;DS:BX, performs the function, returns
;status in carry and AL.
;
;
;Perform the IO, return carry set and an MSDOS
;error code in AL if failure.
;
dtc: push ds ;point ES to
pop es ;cmd block,
push ds
mov ds,cs:dataloc ;set DS,
mov ax,ds ;set cmd addr
mov cx,16
mul cx ;20 bit addr
add ax,offset xcomand
adc dx,0
out cal,al ;out 3 bytes,
call delay
mov al,ah
out cah,al
call delay
mov al,dl
out cat,al
call delay
mov ax,es:dmaseg[bx]
mul cx ;same for data
add ax,es:dmaoff[bx]
adc dx,0
out dal,al ;out 3 bytes,
call delay
mov al,ah
out dah,al
call delay
mov al,dl
out dat,al
call delay
page
;
;Calculate the logical sector, put it in the
;table.
;
mov ax,es:track[bx] ;AX= track,
mov dx,0
mul word ptr es:spt[bx] ; *SPT,
mov cx,es:sector[bx]
dec cx ;make 0-N,
add ax,cx
adc dx,0
mov lowaddr,al ;store low,
mov hiaddr,ah ;high sec,
and dl,0fh ;top sec,
mov al,es:drive[bx] ;make LUN,
mov cl,5
shl al,cl
or al,dl ;add in,
mov lun,al ;store it,
mov control,4 ;type =RMS,
mov ax,es:count[bx]
mov scount,al ;set sec count,
mov al,es:command[bx]
; mov xcomand,xwrite
; cmp al,fwrite
; je dtc1
mov xcomand,xread
cmp al,fread
je dtc1
mov xcomand,xcheck
cmp al,fcheck
je dtc1
stc ;illegal cmd
jmp dtc3
dtc1: in al,ccr ;clear old done
mov al,0
out csr,al ;start it,
dtc2: call delay
in al,csr ;wait for DONE,
test al,80h
jz dtc2
call delay
in al,ccr ;sample ERROR,
and al,2 ;clears DONE,
jz dtc3
stc ;error!
dtc3: pop ds
ret
code ends
end bugger